package br.ufrj.dcc.so.forte.barbearia;

import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.Semaphore;

import br.ufrj.dcc.so.entidades.clientes.Cliente;
import br.ufrj.dcc.so.entidades.clientes.EnumPatente;
import br.ufrj.dcc.so.tela.EstadoBarbearia;
import br.ufrj.dcc.so.tela.Falante;
import br.ufrj.dcc.so.tela.Ouvinte;
import br.ufrj.dcc.so.tela.Tela;

public class BarbeariaRecrutaZero implements Barbearia, Falante {

	private int numOficiais;
	private int numSagentos;
	private int numPracas;
	
	private List<Cliente> oficiaisList;
	private List<Cliente> sargentosList;
	private List<Cliente> pracasList;
	
	private Semaphore oficiaisSemaforo;
	private Semaphore sargentosSemaforo;
	private Semaphore pracasSemaforo;
	
	private Semaphore semaforoFim = new Semaphore(0, true);
	private Semaphore semaforoFazendoRelatorio = new Semaphore(1, true);
	
	private boolean mudouEstado = false;
	
	private boolean fechar = false;
	private int numClientes = 0;

	private List<Funcionario> funcionarios = new ArrayList<Funcionario>();
	private Tela interfaceGrafica = new Tela();
	
	
	
	
	public Tela getInterfaceGrafica() {
		return interfaceGrafica;
	}

	public void setInterfaceGrafica(Tela interfaceGrafica) {
		this.interfaceGrafica = interfaceGrafica;
	}

	@Override
	public void run() {
		try {
			semaforoFim.acquire();
		} catch (InterruptedException e) {
			e.printStackTrace();
		}
		
		alertaFim();
		
		System.exit(0);
		
	}
	
	private void alertaFim() {
		for (Funcionario funcionario : funcionarios) {
			funcionario.fimDoDia();
		}
	}
	
	
	public BarbeariaRecrutaZero(Semaphore oficiaisSemaforo, Semaphore sargentosSemaforo, Semaphore pracasSemaforo) {
		this.oficiaisSemaforo = oficiaisSemaforo;
		this.sargentosSemaforo = sargentosSemaforo;
		this.pracasSemaforo = pracasSemaforo;

		oficiaisList = new ArrayList<Cliente>();
		sargentosList = new ArrayList<Cliente>();
		pracasList = new ArrayList<Cliente>();
		
	}

	
	/**
	 * @see Barbearia#addClienteEspera(Cliente)
	 */
	@Override
	public void addClienteEspera(Cliente cliente) {
		addCliente(escolheLista(cliente.getPatente()), cliente, escolheSemaforo(cliente.getPatente()));
	}
	

	/**
	 * @see Barbearia#pegaProximoCliente(EnumPatente)
	 */
	 @Override 
	public Cliente pegaProximoCliente(EnumPatente patente) {
		 return getCliente(escolheLista(patente), escolheSemaforo(patente));
	}


	private Semaphore escolheSemaforo(EnumPatente patente) {
		Semaphore semaforo = null;
		if (EnumPatente.OFICIAL.equals(patente)) {
			semaforo = oficiaisSemaforo;
			numOficiais++;
		}

		if (EnumPatente.SARGENTO.equals(patente)) {
			semaforo = sargentosSemaforo;
			numSagentos++;
		}

		if (EnumPatente.PRACA.equals(patente)) {
			semaforo = pracasSemaforo;
			numPracas++;
		}
		return semaforo;

	}


	/**
	 * Método que faz a remoção de um cliente de uma dada lista.
	 * @param lista Lista de onde um cliente deve ser removido.
	 * @return O Cliente removido.
	 */
	private Cliente getCliente(List<Cliente> lista, Semaphore semaforo) {
		
		Cliente cliente = null;

		try {
			
			semaforoFazendoRelatorio.acquire();
			// Garanto que os dados da fila não serão modificados enquanto faço a adição do produto.
			semaforo.acquire();
			cliente = lista.remove(0);
			numClientes--;
			mudouEstado = true;
			verificaTermino();
			semaforo.release();
			semaforoFazendoRelatorio.release();
						
		} catch (InterruptedException e) {
			e.printStackTrace();
		}
		
		return cliente;
	}

	private void verificaTermino() {
		if(numClientes==0 && this.fechar){
			semaforoFim.release();
		}
	}

	/**
	 * Método que adiciona um cliente em uma dada lista.
	 * @param lista Lista onde um cliente de ser adicionado.
	 * @param cliente Cliente que deseja ser adicionado.
	 */
	private void addCliente(List<Cliente> lista, Cliente cliente, Semaphore semaforo) {
		
		try {
			//Se estiver fazendo relatório, os valores das listas não podem mudar!
			semaforoFazendoRelatorio.acquire();
			//Garanto que os dados da fila não serão modificados enquanto faço a adição do produto.
			semaforo.acquire();
			lista.add(cliente);
			mudouEstado = true;
			System.out.println("Cliente entrando na barearia\t:"+cliente.getPatente().getPatente());
			numClientes++;
			semaforo.release();
			semaforoFazendoRelatorio.release();
		
		} catch (InterruptedException e) {
			e.printStackTrace();
		}
		
	}
		
	/**
	 * @see Barbearia#possuiPatenteEspera(EnumPatente)
	 */
	@Override
	public boolean possuiPatenteEspera(EnumPatente patente) {
		return (escolheLista(patente).size()>0);
	}
	
	
	/**
	 * Método que escolhe uma lista dada uma patente.
	 * @param patente uma patente 
	 * @return Lista referente a patente passada por param.
	 */
	private List<Cliente> escolheLista(EnumPatente patente) {
		List<Cliente> lista = null;
		if (EnumPatente.OFICIAL.equals(patente)) {
			lista = oficiaisList;
		}

		if (EnumPatente.SARGENTO.equals(patente)) {
			lista = sargentosList;
		}

		if (EnumPatente.PRACA.equals(patente)) {
			lista = pracasList;
		}
		return lista;
	}

	/**
	 * @see Barbearia#addFuncionario(Funcionario...)
	 */
	@Override
	public void addFuncionario(Funcionario... func) {
		for (Funcionario funcionario : func) {
			funcionarios.add(funcionario);
		}
	}

	/**
	 * @see Barbearia#fecha() 
	 */
	@Override
	public void fecha() {
		this.fechar = true;
		verificaTermino();
	}

	/**
	 * @see Barbearia#getEstado()
	 */
	@Override
	public EstadoBarbearia getEstado() throws InterruptedException {
		
		EstadoBarbearia estadoBarbearia = new EstadoBarbearia();
		semaforoFazendoRelatorio.acquire();

		estadoBarbearia.setNumOficiaisFilaEspera(oficiaisList.size());
		estadoBarbearia.setNumSargentosFilaEspera(sargentosList.size());
		estadoBarbearia.setNumPracasFilaEspera(pracasList.size());
		
		estadoBarbearia.setTotalClientes(numClientes);
		
		for (Cliente cliente : oficiaisList) {
			estadoBarbearia.getTempoServicoOficiais().add(cliente.getTempoServico());
		}
		
		for (Cliente cliente : sargentosList) {
			estadoBarbearia.getTempoServicoSargento().add(cliente.getTempoServico());
		}
		
		for (Cliente cliente : pracasList) {
			estadoBarbearia.getTempoServicoPracas().add(cliente.getTempoServico());
		}
		mudouEstado = true;
		interfaceGrafica.consultarDadosRelatorio();
		
		semaforoFazendoRelatorio.release();
		
		
		return estadoBarbearia;
	}
	
	
	public boolean isMudouEstado() {
		return mudouEstado;
	}


	//O valor dessa variável é adicionada de uma unidade toda vez que o método escolheLista é chamado!
	//como ele é chamado duas vezes, uma para addCliente e outr a para getCliente; 
	//vou dividir por 2 para poder entregar seu valor real 
	public int getNumOficiais() {
		return numOficiais/2;
	}

	public int getNumSagentos() {
		return numSagentos/2;
	}

	public int getNumPracas() {
		return numPracas/2;
	}

	@Override
	public void addOuvinte(Ouvinte ouvinte) {
		// TODO Auto-generated method stub
		
	}

	@Override
	public void alertAll() {
		
		
	}
}
